using System.Collections.Generic;
using System.Reflection;
using TestCases.CompareAssert.Core.Comparers.Interfaces;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.Utils.Pairs;

namespace TestCases.CompareAssert.Core.Comparers.Basic
{
    
    public class CircularReferencesComparer : IObjectComparer
    {
        private readonly IList<ICompareContext> _objects = new List<ICompareContext>();

        public CircularReferencesComparer()
        {            
        }

        public CircularReferencesComparer(IObjectComparer comparer)
        {
            Comparer = comparer;
        }

        public IObjectComparer Comparer { get; set; }


        public string Name
        {
            get { return "CircularReferencesComparer"; }
        }

        public IResultBuilder Compare(ICompareContext context)
        {

            var crPair = FindCircularReferences(context);
            if (crPair.Left != null && crPair.Right != null)
            {
                return this.IsEquals(context).
                    Message("<{LPath}> refers to <{LPathParent}> and <{RPath}> refers to <{RPathParent}>.").
                    With(new {LPathParent = crPair.Left.Path, RPathParent = crPair.Right.Path});
            }

            _objects.Add(context);
            var builder = Comparer.Compare(context);
            _objects.RemoveAt(_objects.Count - 1);

            return builder;
        }

        private IPair<IComparerObject, IComparerObject> FindCircularReferences(ICompareContext objects)
        {
            var pair = new ValuePair<IComparerObject, IComparerObject>();
            for (int i = _objects.Count - 1; i >= 0; i--)
            {
                if (ReferenceEquals(_objects[i].LValue, objects.LValue))
                {
                    pair.Left = _objects[i].Left;
                }
                if (ReferenceEquals(_objects[i].RValue, objects.RValue))
                {
                    pair.Right = _objects[i].Right;
                }
                if (pair.Left != null && pair.Right != null)
                {
                    break;
                }
            }
            return pair;
        }
    }
}